<?php
/**
 * Nordic Repository
 * 
 * LICENSE
 * 
 * The new BSD license is applied on this source-file. For further
 * information please visit http://license.nordic-dev.de/newbsd.txt
 * or send an email to andre.moelle@gmail.com.
 */

/**
 * Necessary includes.
 */
Zend_Loader::loadClass('Zend_Date');
Zend_Loader::loadClass('Zend_Auth');
Zend_Loader::loadClass('Zend_Auth_Storage_Session');
Zend_Loader::loadClass('Zend_Auth_Adapter_Interface');

Zend_Loader::loadClass('Nordic_Security');
Zend_Loader::loadClass('Nordic_Auth_Model');

/**
 * Auth-adapter for authenticating in environments using Nordic Repository.
 * 
 * @category   Nordic
 * @package    Nordic_Auth
 * @copyright  2007 Nordic Development
 * @license    http://license.nordic-dev.de/newbsd.txt (New-BSD license)
 * @author     Andre Moelle <andre.moelle@gmail.com>
 * @version    $Id: Auth.php 3 2007-07-08 09:21:42Z andre.moelle $
 */
class Nordic_Auth implements Zend_Auth_Adapter_Interface
{
	/**
	 * Name of the cookie for the persistent login.
	 * 
	 * @const string
	 */
	const COOKIE_NAME = 'persistentLogin';
	
	/**
	 * Delay between two login-attempts.
	 * 
	 * @var integer
	 */
	private $_delay = 30;
	
	/**
	 * Id of the user.
	 * 
	 * @var integer
	 */
	private $_id = 0;
	
	/**
	 * Identifier which is used for permanent logins.
	 * 
	 * @var string
	 */
	private $_identifier;
	
	/**
	 * Model of the nordic-authentication.
	 * 
	 * @var Nordic_Auth_Model
	 */
	private $_model;
	
	/**
	 * Password of the user.
	 * 
	 * @var string
	 */
	private $_password;
	
	/**
	 * Login permanently.
	 * 
	 * @var boolean
	 */
	private $_perm;
	
	/**
	 * Timeout interval for the persistent logins.
	 * 
	 * @var integer
	 */
	private $_timeout = 2592000;
	
	/**
	 * The token used for permanent logins.
	 * 
	 * @var string
	 */
	private $_token;
	
	/**
	 * Username.
	 * 
	 * @var string
	 */
	private $_username;
	
	/**
	 * Constructor accepts the necessary parameters.
	 * 
	 * @param Zend_Db_Adapter_Abstract $db instance of the database
	 * @param array $options data which is probably used by the class
	 * @return void
	 */
	public function __construct ($model, array $options)
	{
		if($model == null)
		{
			$db = Zend_Registry::get('Zend_Db');
			try
			{
				
			$model = new Nordic_Auth_Model(Zend_Registry::get('Zend_Db'));
			}
			catch (Exception $e)
			{
				var_dump($e);
			}
		}
		
		$this->_setModel($model);
		
		$this->_username = gv($options['username'], '');
		$this->_password = gv($options['password'], '');
		$this->_perm = (bool)gv($options['perm'], false);
		$this->_token = gv($options['token'], '');
		$this->_identifier = gv($options['identifier'], '');
		$this->_id = gv($options['id'], 0);
		
		if(!$this->_token && !$this->_identifier && !$this->_id)
		{
			list($this->_id, $this->_identifier, $this->_token) =
				$this->_fetchCookie();
		}
	}
	
	/**
	 * Fetches the cookie and returns their components.
	 * 
	 * If the cookie does not exist, an array with three empty
	 * strings is returned. The result has always the length three.
	 * 
	 * @param void
	 * @return array
	 */
	private function _fetchCookie ()
	{
		if(   !isset($_COOKIE[Nordic_Auth::COOKIE_NAME])
		   || substr_count($_COOKIE[Nordic_Auth::COOKIE_NAME], '.') != 2)
		{
			return array('', '', '');
		}
		
		return explode('.', $_COOKIE[Nordic_Auth::COOKIE_NAME], 3);
	}
	
	/**
	 * Sets a model.
	 * 
	 * @param Nordic_Auth_Model $model new model
	 * @return void
	 */
	private function _setModel (Nordic_Auth_Model $model)
	{
		$this->_model = $model;
	}
	
	/**
	 * Tries to authenticate an user against the nordic-usermanagement.
	 * 
	 * @throws Zend_Auth_Adapter_Exception
	 * @return Zend_Auth_Result
	 */
	public function authenticate ()
	{
		if( (!$this->_token
			|| !$this->_identifier)
			&& (!$this->_password 
			|| !$this->_username))
		{
			return new Zend_Auth_Result(
				false,
				'',
				(array)'Please enter an username with the corresponding password.'
			);
		}
		
		if($this->_username && $this->_password)
		{
			$messages = $this->login();
		}
		else
		{
			$messages = $this->autoLogin();
		}
		
		if(!$messages)
		{			
			if($this->_perm)
			{
				$this->persist();
			}
			
			return new Zend_Auth_Result(
				true,
				$this->_id,
				array()
			);
		}
		
		return new Zend_Auth_Result(
			false,
			0,
			$messages
		);
	}
	
	/**
	 * Logs the user in using token and identifier.
	 * 
	 * Result is an array which contains error-messages on failure.
	 * 
	 * @param void
	 * @return array
	 */
	public function autoLogin ()
	{
		$row = $this->_model->getToken()->findToken($this->_id);
		
		if(   $this->_identifier != $row['identifier']
		   || $this->_token != $row['token'])
		{
			$this->_model->getToken()->deleteToken($this->_id);
			
			return (array)'Token or identifier does not match';
		}
		
		$this->_model->getToken()->deleteToken($this->_id);
		setcookie(self::COOKIE_NAME, '', -1, '/');
		$this->_perm = true;
		
		return array();
	}
	
	/**
	 * Returns the model.
	 * 
	 * @param void
	 * @return Nordic_Auth_Model
	 */
	public function getModel ()
	{
		return $this->_model;
	}
	
	/**
	 * Logs the user in using his credentials.
	 * 
	 * Result is an array which contains error-messages on failure.
	 * 
	 * @param void
	 * @return array
	 */
	public function login ()
	{
		$row = $this->_model->getUser()->findByUsername($this->_username);
		
		if(!$row)
		{
			return array('You have entered an unknown username.');
		}
		
		$date = new Zend_Date(
			$row->lasttry,
			Zend_Date::ISO_8601
		);
		
		if(   !$row->checkPassword($this->_password)
		   || Zend_Date::now()->sub($date) <= $this->_delay)
		{
			$this->_model->getUser()->updateTry($row->id);
			
			return array('Password is wrong. Please wait 30s before trying again to log in your account.');
		}
		
		$this->_model->getUser()->updateLogin($row->id);
		$this->_id = $row->id;
		
		return array();
	}
	
	/**
	 * Makes the login persistent.
	 * 
	 * @param void
	 * @return boolean
	 */
	public function persist ()
	{
		$identifier = Nordic_Security::createString();
		$token = Nordic_Security::createString();
		
		setcookie(
			self::COOKIE_NAME,
			$this->_id . '.' . $identifier . '.'  . $token,
			time() + $this->_timeout,
			'/'
		);
		
		return $this->_model->getToken()
			 ->setToken($this->_id, $identifier, $token, $this->_timeout);
	}
}
?>